﻿using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Windows.Forms;

namespace SlabInspection
{
    //空间点的坐标
    public struct Point3D
    {
        public double x, y, z;
        public Point3D(double x, double y, double z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public static Point3D operator +(Point3D p1, Point3D p2)
        {
            return new Point3D(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
        }

        public static Point3D operator /(Point3D p1, int n)
        {
            return new Point3D(p1.x / n, p1.y / n, p1.z / n);
        }

    }

    class CSlab
    {
        //平面方程aX+bY+cZ+d=0
        public struct EquationParameter
        {
            public double A, B, C, D;
            public EquationParameter(double a, double b, double c, double d)
            {
                this.A = a;
                this.B = b;
                this.C = c;
                this.D = d;
            }
        }

        public Point3D[] points;//点坐标的结构体数组
        public Point3D[] footPoints;//所有点到基准平面的垂足坐标
        public Point3D[] _MiddleFootPoints;//所有投影点，每行坐标和除以4得到中线点坐标
        public Point3D[,] footLinePoints;
        public ArrayList Equations = new ArrayList();
        public int EquationNumber = -1;//方程ID
        public int p1;//确定平面方程的三个基点
        public int p2;
        public int p3;
        public int nRows;
        public int[,] p2ps = new int[4, 2];
        public double[,] footLineOffsets;//四列的偏移量
        public double[] PlaneOffset;//其它个点到标准面的偏差
        public double[,] footSymmeOffsets;//所有点到最优中线的距离,用于判断对称性
        public double[] ange = new double[4];//空间直线的平行性,用于判断对称性

        public Point3D[] _bestMiddleTwoPoint = new Point3D[2];
        public Point3D[] ShelfPoints = new Point3D[6];//标架坐标
        public Point3D[] footShelfPoints = new Point3D[6];
        public Point3D[] _middleFootShelfPoint = new Point3D[2];//1 2 3 4号标架投影点中线坐标
        public Point3D[] _middleFootCrossPoint = new Point3D[2];//5号点到1 2号的垂足，6号点到3 4号的垂足，
        public double[] ShelfOffsets = new double[2];//
        public double[] _ShelfAngle = new double[2];//标架夹角




        public CSlab(Point3D[] ps)
        {
            points = new Point3D[ps.Length - 6];
            footPoints = new Point3D[ps.Length - 6];
            PlaneOffset = new double[ps.Length - 6];

            nRows = (ps.Length - 6) / 4;
            _MiddleFootPoints = new Point3D[nRows];
            footLinePoints = new Point3D[4, nRows];
            footLineOffsets = new double[4, nRows];
            footSymmeOffsets = new double[4, nRows];

            Array.Copy(ps, points, ps.Length - 6);
            Array.Copy(ps, ps.Length - 6, ShelfPoints, 0, 6);

        }

        public void Analysis()
        {
            _AnalysisFlatness();
            _AnalysisLinearity();
        }

        /// <summary>
        /// 分析32个点的共面性，并应该先于直线性分析、对称性分析之前被执行
        /// </summary>
        private void _AnalysisFlatness()
        {
            //32个点任取3点的组合4960个平面
            ArrayList pointEquations = Utility.GetCombination(points.Length, 3);

            double[] ddd = new double[4];
            //计算出所有的平面方程
            for (int i = 0; i < pointEquations.Count; i++)
            {
                s3Point p3p = (s3Point)pointEquations[i];
                //求出平面方程aX+bY+cZ+d=0;
                ddd = Geometry.PanelEquationFromThreePt(points[p3p.p1], points[p3p.p2], points[p3p.p3]);
                Equations.Add(new EquationParameter(ddd[0], ddd[1], ddd[2], ddd[3]));//把所有的方程系数存入
            }

            //计算出所有的标准差，
            ArrayList stdAL = new ArrayList();
            for (int i = 0; i < Equations.Count; i++)
            {
                double a = ((EquationParameter)Equations[i]).A;
                double b = ((EquationParameter)Equations[i]).B;
                double c = ((EquationParameter)Equations[i]).C;
                double d = ((EquationParameter)Equations[i]).D;
                double ff = 0.0;
                for (int j = 0; j < points.Length; j++)
                {
                    ff += Geometry.DistPt2Panel(points[j], a, b, c, d);
                }
                stdAL.Add(ff);
            }

            //找到最小的标准差
            double min = Convert.ToDouble(stdAL[0].ToString());
            int lowID = 0;
            for (int i = 1; i < stdAL.Count; i++)
            {
                if (Convert.ToDouble(stdAL[i]) < min)
                {
                    min = Convert.ToDouble(stdAL[i]);
                    lowID = i;
                }
            }

            EquationNumber = lowID;//方程ID
            {
                p1 = ((s3Point)pointEquations[lowID]).p1;
                p2 = ((s3Point)pointEquations[lowID]).p2;
                p3 = ((s3Point)pointEquations[lowID]).p3;
            }

            _footPoints();//求出所有点的投影坐标

            {
                double a = ((EquationParameter)Equations[lowID]).A;
                double b = ((EquationParameter)Equations[lowID]).B;
                double c = ((EquationParameter)Equations[lowID]).C;
                double d = ((EquationParameter)Equations[lowID]).D;

                for (int i = 0; i < points.Length; i++)
                {
                    if (points[i].z >= footPoints[i].z)
                    {
                        PlaneOffset[i] = Geometry.DistPt2Panel(points[i], a, b, c, d);
                    }
                    else
                    {
                        PlaneOffset[i] = -Geometry.DistPt2Panel(points[i], a, b, c, d);
                    }
                }
            }
        }

        /// <summary>
        /// 直线性分析
        /// </summary>
        private void _AnalysisLinearity()
        {
            //在每条直线单独计算时，8个点任取2点的组合条直线
            //计算出所有的直线方程点组合,各条直线的组合方法是一样的
            ArrayList pointEquations = Utility.GetCombination(nRows, 2);

            SortedList sl = new SortedList();
            double sum = 0.0;
            double d = 0.0;
            s2Point p2p;
            int p1;
            int p2;

            for (int m = 0; m < 4; m++)//四条直线
            {
                sl.Clear();
                for (int i = 0; i < pointEquations.Count; i++)//某条直线的组合
                {
                    p2p = (s2Point)pointEquations[i];
                    sum = 0.0;
                    for (int j = 0; j < nRows - 1; j++)
                    {

                        if (j == p2p.p1 || j == p2p.p2) continue;
                        d = Geometry.Pt2LineByTrigonometric(footLinePoints[m, p2p.p1], footLinePoints[m, p2p.p2], footLinePoints[m, j]);
                        sum += d * d;
                    }
                    sl.Add(sum, p2p);
                }

                p1 = ((s2Point)sl.GetByIndex(0)).p1;//sl.GetByIndex(0),取得最小
                p2 = ((s2Point)sl.GetByIndex(0)).p2;
                p2ps[m, 0] = p1;
                p2ps[m, 1] = p2;
                for (int i = 0; i < nRows; i++)
                {
                    footSymmeOffsets[m, i] = Geometry.Pt2Pt(footLinePoints[m, i], Geometry.GetPoint2LineCrossPoint(_bestMiddleTwoPoint[0], _bestMiddleTwoPoint[1], footLinePoints[m, i]));

                    footLineOffsets[m, i] = footSymmeOffsets[m, i] - Geometry.Pt2Pt(Geometry.GetPoint2LineCrossPoint(_bestMiddleTwoPoint[0], _bestMiddleTwoPoint[1], footLinePoints[m, i]),
                        Geometry.GetPoint2LineCrossPoint(footLinePoints[m, p1], footLinePoints[m, p2], footLinePoints[m, i]));
                }
            }
            GetAllCrossAngle();
        }

        //求所有空间点到基准平面的投影坐标。
        private void _footPoints()
        {
            double a = ((EquationParameter)Equations[EquationNumber]).A;
            double b = ((EquationParameter)Equations[EquationNumber]).B;
            double c = ((EquationParameter)Equations[EquationNumber]).C;
            double d = ((EquationParameter)Equations[EquationNumber]).D;
            double t;
            Point3D p;
            for (int i = 0; i < points.Length; i++)
            {
                p = points[i];
                t = -(d + p.x * a + p.y * b + p.z * c) / (a * a + b * b + c * c);//求出参数t
                footPoints[i].x = p.x + a * t;
                footPoints[i].y = p.y + b * t;
                footPoints[i].z = p.z + c * t;
            }

            for (int i = 0; i < nRows; i++)
            {
                footLinePoints[0, i] = footPoints[i * 4 + 0];
                footLinePoints[1, i] = footPoints[i * 4 + 1];
                footLinePoints[2, i] = footPoints[i * 4 + 2];
                footLinePoints[3, i] = footPoints[i * 4 + 3];
            }

            //中线投影点坐标
            for (int i = 0; i < _MiddleFootPoints.Length; i++)
            {
                _MiddleFootPoints[i] = (footPoints[i * 4 + 0] + footPoints[i * 4 + 1] + footPoints[i * 4 + 2] + footPoints[i * 4 + 3]) / 4;
            }

            //标架投影点
            for (int i = 0; i < footShelfPoints.Length; i++)
            {
                p = ShelfPoints[i];
                t = -(d + p.x * a + p.y * b + p.z * c) / (a * a + b * b + c * c);//求出参数t
                footShelfPoints[i].x = p.x + a * t;
                footShelfPoints[i].y = p.y + b * t;
                footShelfPoints[i].z = p.z + c * t;
            }

            //1 2 3 4号标架投影点中线坐标
            {
                _middleFootShelfPoint[0] = (footShelfPoints[0] + footShelfPoints[1]) / 2;
                _middleFootShelfPoint[1] = (footShelfPoints[2] + footShelfPoints[3]) / 2;
            }
            _GetBestMiddlePoint();//不用计算出的最佳中线两点,用实测的5、6号标架
            _GetShelfAngle();
        }

        private void _GetShelfAngle()
        {
            _ShelfAngle[0] = Math.Round(Geometry.GetCrossAngle(footShelfPoints[0], footShelfPoints[1], footShelfPoints[4], footShelfPoints[5]) * (180 / Math.PI), 2);
            _ShelfAngle[1] = Math.Round(Geometry.GetCrossAngle(footShelfPoints[2], footShelfPoints[3], footShelfPoints[4], footShelfPoints[5]) * (180 / Math.PI), 2);
            _middleFootCrossPoint[0] = Geometry.GetPoint2LineCrossPoint(footShelfPoints[0], footShelfPoints[1], footShelfPoints[4]);
            _middleFootCrossPoint[1] = Geometry.GetPoint2LineCrossPoint(footShelfPoints[2], footShelfPoints[3], footShelfPoints[5]);
            if (_middleFootCrossPoint[0].x - _middleFootShelfPoint[0].x < 0)
            {
                ShelfOffsets[0] = Math.Round(-Geometry.Pt2Pt(_middleFootShelfPoint[0], _middleFootCrossPoint[0]),2);
            }
            else
            {
                ShelfOffsets[0] = Math.Round(Geometry.Pt2Pt(_middleFootShelfPoint[0], _middleFootCrossPoint[0]), 2);
            }
            if (_middleFootCrossPoint[1].x - _middleFootShelfPoint[1].x < 0)
            {
                ShelfOffsets[1] = Math.Round(-Geometry.Pt2Pt(_middleFootShelfPoint[1], _middleFootCrossPoint[1]), 2);
            }
            else
            {
                ShelfOffsets[1] = Math.Round(Geometry.Pt2Pt(_middleFootShelfPoint[1], _middleFootCrossPoint[1]), 2);
            }
        }

        //求确定最佳中线的两个点
        private void _GetBestMiddlePoint()
        {
            //ArrayList pointEquations = Utility.GetCombination(nRows, 2);

            //SortedList sl = new SortedList();
            //double sum = 0.0;
            //double d = 0.0;
            //s2Point p2p;
            //int p1;
            //int p2;
            //for (int i = 0; i < pointEquations.Count; i++)//某条直线的组合
            //{
            //    p2p = (s2Point)pointEquations[i];
            //    sum = 0.0;
            //    for (int j = 0; j < nRows - 1; j++)
            //    {

            //        if (j == p2p.p1 || j == p2p.p2) continue;
            //        d = Geometry.Pt2LineByTrigonometric(_MiddleFootPoints[p2p.p1], _MiddleFootPoints[p2p.p2], _MiddleFootPoints[j]);
            //        sum += d * d;
            //    }
            //    sl.Add(sum, p2p);
            //}
            //p1 = ((s2Point)sl.GetByIndex(0)).p1;//sl.GetByIndex(0),取得最小
            //p2 = ((s2Point)sl.GetByIndex(0)).p2;

            //_bestMiddleTwoPoint[0] = _MiddleFootPoints[p1];
            //_bestMiddleTwoPoint[1] = _MiddleFootPoints[p2];
            _bestMiddleTwoPoint = _middleFootShelfPoint;
            //Array.Copy(_bestMiddleTwoPoint, _bestFootShelfPoint,2);
            // Array.Copy(ShelfPoints, 4, _bestMiddleTwoPoint, 0, 2);
        }

        //得到四条直线和最佳中线的夹角
        public void GetAllCrossAngle()
        {
            for (int i = 0; i < p2ps.GetLength(0); i++)
            {
                ange[i] = Geometry.GetCrossAngle(_bestMiddleTwoPoint[0], _bestMiddleTwoPoint[1], footLinePoints[i, p2ps[i, 0]], footLinePoints[i, p2ps[i, 1]]) * (180 / Math.PI) * 3600;
                ange[i] = Math.Round(ange[i], 4);
            }
        }

        /// <summary>
        /// 得到标架夹角数组
        /// </summary>
        /// <returns></returns>
        public double[] GetShelfAngle()
        {
            return _ShelfAngle;
        }
    }
}
